# 220128 2053
# 内建函数
# 用户自定义函数
# def关键字

def hello():
	print('hello world')

hello();

print("end.1")

def max(a, b):
	'''
	返回大的值
	'''
	if a > b:
		return a
	else:
		return b

print(max(4,5))
print(max(100.12,12.2001))
print(max('abc', 'def')) # def

print("end.2")

def area(width, height):
	'''
	计算矩形面积
	'''
	return width * height

print(area(12, 23))

print("end.3")

# 参数传递
# 在 python 中，类型属于对象，变量是没有类型的
# 可更改(mutable)与不可更改(immutable)对象
# 在 python 中，strings, tuples, 和 numbers 是不可更改的对象，
# 而 list,dict 等则是可以修改的对象。

# 不可变类型：类似 C++ 的值传递
# 可变类型：类似 C++ 的引用传递

def change(a):
	'''
	不可变对象传递
	'''
	print(id(a))
	a = 10
	print(id(a)) # 变了

a = 1
print(id(1))
change(a)
print(id(1))

print("end.4")

def changeMe(mylist):
	'''
	可变对象传递
	'''
	mylist.append([1,2,3,4])
	print('1 mylist: ', mylist)
	return

mylist = [10, 20, 30]
changeMe(mylist)
print('2 mylist: ', mylist)

print("end.5")

# 参数
'''
调用函数时可使用的正式参数类型：
必需参数
关键字参数
默认参数
不定长参数
'''

# 必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

# 关键字参数和函数调用关系紧密，函数调用使用关键字参数来确定传入的参数值。

def printinfo(name, age):
	'''
	关键字参数
	'''
	print('名字：', name)
	print('年龄：', age)
	return

printinfo(age=100, name='ben')

print("end.6")

# 调用函数时，如果没有传递参数，则会使用默认参数。

def printinfo2(name, age = 36):
	'''
	默认值参数
	'''
	print('名字：', name)
	print('年龄：', age)
	return

printinfo2(age= 99, name='work3')
printinfo2(name='work4') # 使用默认值

print("end.7")

# 不定长参数
# def functionname([formal_args,] *var_args_tuple ):
# 加了星号 * 的参数
# 以元组(tuple)的形式导入

def printinfo3(arg1, *vartuple):
	"打印任何传入的参数"
	print('OUTPUT:')
	print(arg1)
	print(vartuple)

printinfo3(70, 80, 90, 'abc')

# 可以不向函数传递未命名的变量

print("end.8")

# 还有一种就是参数带两个星号 **基本语法如下：
# def functionname([formal_args,] **var_args_dict ):
# 加了两个星号 ** 的参数会以字典的形式导入。

# 声明函数时，参数中星号 * 可以单独出现
# 如果单独出现星号 * 后的参数必须用关键字传入。
# def f(a,b,*,c):

print("end.9")

# 匿名函数
# 使用 lambda 来创建匿名函数
# lambda [arg1 [,arg2,.....argn]]:expression

# 函数sum
sum = lambda arg1, arg2: arg1 + arg2

print(sum(10,20)) # ok
#print(sum('a',20)) # TypeError: can only concatenate str (not "int") to str
print(sum('a','\'20--\"')) # ok

print("end.10")

# return语句
# 用于退出函数
# 不带参数值的return语句返回None
#...

# 强制位置参数
# Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数，
# 不能使用关键字参数的形式。

# 其它
# 默认参数必须放在最后面

# 对于变量作用域，
# 变量的访问以 
# L（Local） –> E（Enclosing） –> G（Global） –>B（Built-in） 的规则查找

# 函数内可以访问全局变量，但不能更新(修改)其值！

a174 = 10
def sum175(n):
	global a174
	n += a174 # # UnboundLocalError: local variable 'a174' referenced before assignment
	a174 = 100 # error
	print('a174=', a174, end = ', ')
	print('n=', n)

sum175(3)

# 可以加上 global 引用以更新变量值 
# 使用下面的即可更改
# global a174

print("end.11") 

# 函数也可以以一个函数为其参数

# 通过 函数名.__doc__ 的方式来显示函数的说明文档

# Python 函数可以返回多个值，多个值以元组的方式返回

# 函数的装饰器
# 回调函数和返回函数的实例就是装饰器。

def log198(pr):
	def wrapper():
		print('------')
		return pr() # 执行被装饰的函数
	return wrapper

@log198
def pr():
	print('pr function body')

pr()

print("end.12")

# 关于 global 和 nonlocal 的知识
# nonlocal 只能修改外层函数的变量而不能修改外层函数所引用的全局变量
# global 关键字会跳过中间层直接将嵌套作用域内的局部变量变为全局变量

# 函数的参数分为形参和实参

# 在编写函数的过程中，可以显式指定函数的参数类型及返回值类型
def function_demo(param_A: int, param_B: float, param_C: list, param_D: tuple) -> dict:
	pass
# 将数据类型写死在代码中
